package com.example.demo.common.aop;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.Logger;
import reactor.util.Loggers;

import java.net.InetAddress;
import java.util.Arrays;
import java.util.function.Consumer;

@Aspect
@Configuration
@RequiredArgsConstructor
public class WebAspect {

    private final static Logger log = Loggers.getLogger(WebAspect.class);
    private ObjectMapper objectMapper;

    @Before(value = "execution(public * com.example.demo..*.controller..*.*(..)) && args(webExchange, ..)")
    public void doBefore(JoinPoint joinPoint, ServerWebExchange webExchange) {
        ServerHttpRequest httpRequest = webExchange.getRequest();
        StringBuilder sb = new StringBuilder("\n");
        sb.append("=>\n");
        sb.append("URL            :").append(httpRequest.getURI()).append("\n");
        sb.append("HTTP Method    :").append(httpRequest.getMethod()).append("\n");
        sb.append("Class Method   :").append(joinPoint.getSignature().getDeclaringTypeName()).append(".").append(joinPoint.getSignature().getName()).append("\n");
        sb.append("IP             :").append(getIp(httpRequest)).append("\n");
        sb.append("Request Args   :").append(Arrays.toString(joinPoint.getArgs())).append("\n");
        log.info(sb.toString());
    }

    @Around("execution(public * com.example.demo..*.controller..*.*(..))")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long t = System.currentTimeMillis();
        Object ob = joinPoint.proceed();
        log.info("<= {} :: Execution Time: {}ms", joinPoint.getSignature().toShortString(), System.currentTimeMillis() - t);
        return ob;
    }

    @AfterReturning(value = "execution(public * com.example.demo..*.controller..*.*(..)) && args(webExchange, ..)", returning = "ret")
    public void doAfterReturning(Object ret, ServerWebExchange webExchange) {
        if(ret instanceof Mono){
            Mono<?> retVal = (Mono<?>)ret;
            retVal.subscribe((Consumer<Object>) o -> {
                try {
                    String text = o instanceof String ? o.toString() : objectMapper.writeValueAsString(ret);
                    log.info("<= {} response : {} " ,webExchange.getRequest().getURI(), text);
                } catch (JsonProcessingException e) {}
            }).dispose();
        }else if(ret instanceof Flux){
//            Flux<?> retVal = (Flux<?>)ret;
//            retVal.doOnNext((Consumer<Object>) o -> {
//                try {
//                    String text = o instanceof String ? o.toString() : objectMapper.writeValueAsString(ret);
//                    log.info("\n{} \nresponse : {} ", webExchange.getRequest().getURI(), text);
//                } catch (JsonProcessingException e) {}
//            }).;
        }
    }

    @AfterThrowing(value = "execution(public * com.example.demo..*.controller..*.*(..)) && args(webExchange, ..)", throwing = "throwable")
    public void doAfterThrowing(Throwable throwable, ServerWebExchange webExchange) {
        ServerHttpRequest httpRequest = webExchange.getRequest();
        log.error("\n{}接口调用异常，异常信息{}", httpRequest.getURI(), throwable.getMessage());
    }

    /**
     * 获取ip地址
     * @param request 请求
     * @return
     */
    public String getIp(ServerHttpRequest request){
        HttpHeaders headers = request.getHeaders();
        String ip = headers.getFirst("x-forwarded-for");
        if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个ip值，第一个ip才是真实ip
            if (ip.indexOf(",") != -1) {
                ip = ip.split(",")[0];
            }
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("X-Real-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddress().getAddress().getHostAddress();
        }
        if(isLocalIp(ip)){
            InetAddress inet = null;
            try {
                inet = InetAddress.getLocalHost();
            } catch (Exception var4) {
                var4.printStackTrace();
            }

            ip = inet.getHostAddress();
        }
        return ip.replaceAll(":", ".");
    }

    /**
     * 是否为本地ip
     *
     * @param ipAddr
     * @return
     */
    public static boolean isLocalIp(String ipAddr) {
        return "127.0.0.1".equals(ipAddr) || "0:0:0:0:0:0:0:1".equals(ipAddr);
    }

}
